KarL05/Aiyiyi's Blog
IOI 2022 Circuit 题解

IOI2022 Circuit 题解

题目链接: https://loj.ac/p/3833

通过人数:~100/357

题目难度:CF1800

观察

题目的描述看起来非常开幕雷击 - 这怎么可能能做呢?这个看起来像动态规划的问题凭什么可以区间修改? - 估计隐藏了一些条件,那么这题的目的就是找到隐藏的条件了。

解答

首先注意到 DAG 和树在本问题上没有区别。因此考虑一个树。

对于 DAG 中的任意一个节点 u, 若令该节点不亮的参数选择有 \(a_u\) 种,令该节点不亮的参数选择有 \(b_u\) 种, 令集合 \(S(u)\) 包括所有存在边 (u,v) 的点 v, 且定义函数 \(f(v)\)

\[f(v) = (\prod_{s \in S(u)} (a_s+b_s))\times b_v\]

(设一个点的点权为他的同父亲节点的点的 \((a+b)\) 之积和该点的 \(b\) 的乘积)

\(b_u\) 满足以下递归式

\[b_u = \sum_{s \in S(u)} f(v)\]

(对于 \(b_u\), 可以分解为 \(u\) 的所有相邻点(除父亲节点外)的权值之和)

观察到 \((a_i+b_i)\) 为常数,将 \(b_1\) 即所求答案的表达式打开可以得到叶子节点的贡献对于 \(b_1\) 加法独立。注意到叶子节点 \(b_l = 1\) 是亮,\(a_l = 0\) 是不亮。线段树维护即可(维护 \(\sum g(i)\), 也就是对于每个叶子维护单独的贡献之和)。

证明

根据组合意义显然。

也可以考虑计算的方式,考虑生成函数 \(f_i(x) = (a_i+b_ix)\), 则 \(b_u\) 可以表达为一些 \(f_i(x)\) 的积的系数的带权和。(也就是对于每种亮和不亮的选择,我可以选择权值使得当前的节点亮必须要小于等于亮的节点个数)。权与 \(x\) 的幂次相关(因为幂次是亮的灯的个数),因此考虑求导。

所以 \(b_u\) 的递归式可以写成如下的形式。

\[ b_u = \sum_{c} [x^c] \frac{\text{d}}{\text{d}x}\prod (a_i+b_ix) \]

\[ b_u = [x^{p-1}] \frac{1}{1-x} \frac{\text{d}}{\text{d}x}\prod (a_i+b_ix) \]

\[ b_u = [x^{p-1}] \frac{1}{1-x} \sum_i\frac{b_i}{a_i+b_ix}\prod_{j} (a_j+b_jx) \]

注意到我们要求的是 \(x\) 的 1-p 次幂的系数和。其中 p 为度数。但左侧生成函数的最高次只有 \(p\), 因此所有的 \(x\) 都可以取 1 (现在可以随便选也满足要求,限制求导后就消失了,x=1 就去掉生成函数了).

\[ b_u = \sum_i\frac{b_i}{a_i+b_i}\prod_{j} (a_j+b_j) \]

变形后可以得到前面的公式。证毕。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include"circuit.h"
#include"bits/stdc++.h"
#define ll long long
#define ls p+p
#define rs p+p+1
using namespace std;

const int maxn = 2e5+5;
vector<int> E[maxn];
vector<ll> pre[maxn], suf[maxn];
ll val[maxn]; ll deg[maxn];
ll on[maxn]; ll ans[maxn];
ll cnt[maxn];
int n,m,rt = 1;
const int MOD = 1e9+2022;

struct seg {
int l, r;
ll cur, oth;
int tag;
};
seg t[maxn*4];

void pushup (int p) {
t[p].cur = (t[ls].cur+t[rs].cur)%MOD;
t[p].oth = (t[ls].oth+t[rs].oth)%MOD;
}

void pushdown (int p) {
if (!t[p].tag) return;
t[p].tag = 0;
t[ls].tag ^= 1;t[rs].tag ^= 1;
swap(t[ls].cur,t[ls].oth);
swap(t[rs].cur,t[rs].oth);
}

void build (int p, int l, int r) {
t[p].l = l;
t[p].r = r;
if (l==r) {
if (on[l]) t[p].cur = ans[l+n];
else t[p].oth = ans[l+n];
return;
}
int mid = (l+r)/2;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(p);
}

int query () {
return t[1].cur%MOD;
}

void update (int p, int l, int r) {
if (l<=t[p].l && t[p].r<=r) {
swap(t[p].cur,t[p].oth);
t[p].tag ^= 1;
return;
}
pushdown(p);
int mid = (t[p].l+t[p].r)/2;
if (l<=mid) update(ls,l,r);
if (mid<r) update(rs,l,r);
pushup(p);
}

int DFS1 (int u) {
val[u] = max(1ll,deg[u]);
for (int v:E[u]) {
val[u] *= DFS1(v);
val[u] %= MOD;
}
return val[u];
}

void DFS2 (int u, ll prod) {
ans[u] = prod;
for (int i=0;i<E[u].size();i++) {
ll nprod = prod;
int v = E[u][i];
if (i>0) {
nprod *= pre[u][i-1];
nprod %= MOD;
}
if (i<E[u].size()-1) {
nprod *= suf[u][i+1];
nprod %= MOD;
}
DFS2(v,nprod);
}
}
void init (int N, int M, vector<int> f, vector<int> v) {
n = N; m = M;
for (int i=0;i<f.size();i++) {
E[f[i]+1].push_back(i+1);
pre[f[i]+1].push_back(1);
suf[f[i]+1].push_back(1);
deg[f[i]+1]++;
}
for (int i=0;i<v.size();i++) {
on[i+1] = v[i];
}
DFS1(rt);
for (int i=1;i<=n+m;i++) {
for (int j=0;j<E[i].size();j++) {
pre[i][j] = val[E[i][j]];
suf[i][j] = val[E[i][j]];
}
for (int j=1;j<E[i].size();j++) {
pre[i][j] *= pre[i][j-1];
pre[i][j] %= MOD;
}
for (int j=E[i].size()-2;j>=0;j--) {
suf[i][j] *= suf[i][j+1];
suf[i][j] %= MOD;
}
}
DFS2(rt,1);
build(1,1,m);
}

int count_ways (int l, int r) {
l++;r++;l-=n;r-=n;
update(1,l,r);
return query();
}